﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using Api = Authorit.API;

namespace DitaImport
{
	/// <summary>
	/// Manages the import of DITA content into an Author-it library
	/// </summary>
	/// <remarks>Typically a single instance of this class will create and dispose many instances of the <see cref="DitaTopic"/> class</remarks>
	public class Importer
	{
		#region Fields (14) 

		/// <summary>
		/// Context object for Author-it
		/// </summary>
		private Api.ApplicationContext _applicationContext;
		/// <summary>
		/// Authentication service
		/// </summary>
		private Api.Services.Authentication _authentication;
		/// <summary>
		/// Object cache
		/// </summary>
		/// <remarks>Indexed by GUID.</remarks>
		private Dictionary<Guid, Api.Dto.ObjectBrief> _cachedObjectDto = new Dictionary<Guid, Api.Dto.ObjectBrief>();
		/// <summary>
		/// Stores the folder in which to create imported content.
		/// </summary>
		private int importFolderId = 0;
		/// <summary>
		/// Stores the full path to the file from which the objects are imported.
		/// </summary>
		/// <remarks>The plugin stores this value in the Author-it &lt;IMPORT_FULL_PATH&gt; variable.</remarks>
		private Api.Dto.Variable importFullPathVariable;
		// Variables (and variants) used by the importer
		/// <summary>
		/// Stores the import ID for an object
		/// </summary>
		/// <remarks>The plugin stores this value in the Author-it &lt;IMPORT_ID&gt; variable.</remarks>
		private Api.Dto.Variable importIdVariable;
		/// <summary>
		/// Represents a topic for importing
		/// </summary>
		private DitaTopic importTopic;
		/// <summary>
		/// Object ID cache
		/// </summary>
		private Dictionary<Guid, int> objectIdCache = new Dictionary<Guid, int>();
		/// <summary>
		/// Release state cache
		/// </summary>
		private Dictionary<Guid, Api.Dto.ReleaseState> releaseStateCache = new Dictionary<Guid, Api.Dto.ReleaseState>();
		/// <summary>
		/// Service for managing books
		/// </summary>
		private Api.Services.Book svcBook;
		// Services for retrieving and creating specific AIT objects
		/// <summary>
		/// Service for common Author-it object methods
		/// </summary>
		private Api.Services.ObjectCommon svcCommon;
		/// <summary>
		/// Service for managing hyperlinks
		/// </summary>
		private Api.Services.Hyperlink svcHyperlink;
		/// <summary>
		/// Service for managing topics
		/// </summary>
		private Api.Services.Topic svcTopic;
		/// <summary>
		/// Service for managing variables
		/// </summary>
		private Api.Services.Variable svcVariable;
		/// <summary>
		/// Backing field for the <see cref="ConfigSettings"/> property
		/// </summary>
		private Config _config;
		#endregion Fields 

		#region Enums (1) 

		/// <summary>
		/// Specifies the severity of logged information
		/// </summary>
		public enum Severity : int
		{
			/// <summary>
			/// Information only
			/// </summary>
			Information = 1,

			/// <summary>
			/// Non-fatal error
			/// </summary>
			Warning = 2,

			/// <summary>
			/// Fatal error
			/// </summary>
			Error = 3
		}

		#endregion Enums 

		#region Constructors (2) 

		/// <summary>
		/// Initializes a new instance of the <see cref="Importer"/> class.
		/// </summary>
		/// <param name="ApplicationContext">Author-it connection context</param>
		/// <param name="LibraryAlias">Alias for the Author-it library</param>
		/// <param name="UserName">Author-it username</param>
		/// <param name="Password">Author-it password</param>
		/// <param name="configuration">Configuration object</param>
		public Importer(Api.ApplicationContext ApplicationContext, string LibraryAlias, string UserName, string Password, Config configuration)
		{
			// connect to library using information in proxy
			_applicationContext = ApplicationContext;  //conTODO: Get application context using PluginUtility
			_authentication = _applicationContext.GetServiceInstance<Api.Services.Authentication>();
			_config = configuration;

			Api.Dto.LoginResponse response = _authentication.Login(LibraryAlias, UserName, Password);
			if (response.Result != Api.Dto.LoginResult.Success)
			{
				// Failed to access database, or login to library
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, string.Format("Cannot connect: {0}", response.ToString())));
				throw new Exception(string.Format("Cannot connect: {0}", response.ToString()));
			}

			// create all the services required by the importer
			try
			{
				svcCommon = _applicationContext.GetServiceInstance<Api.Services.ObjectCommon>();
				svcTopic = _applicationContext.GetServiceInstance<Api.Services.Topic>();
				svcBook = _applicationContext.GetServiceInstance<Api.Services.Book>();
				svcHyperlink = _applicationContext.GetServiceInstance<Api.Services.Hyperlink>();
				svcVariable = _applicationContext.GetServiceInstance<Api.Services.Variable>();
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, "Error creating SDK Services", ex));
			}

			// check that the variables required by the importer exist in the library
			try
			{
				importIdVariable = svcVariable.GetByName("IMPORT_ID");
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, "The variable <IMPORT_ID> is missing from the library.", ex));
			}
			try
			{
				importFullPathVariable = svcVariable.GetByName("IMPORT_FULL_PATH");
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, "The variable <IMPORT_FULL_PATH> is missing from the library.", ex));
			}

			//TODO: check all the objects required by the DITA import exist in  the library

			importTopic = new DitaTopic(this);

			// Add event handlers
			importTopic.ObjectModified += TopicImportModified;
			importTopic.ObjectCreated += TopicImportCreated;
			importTopic.Log += TopicImportLog;
		}

		/// <summary>
		/// Destructor for the <see cref="Importer"/> class.
		/// </summary>
		~Importer()
		{
			LogOut();  //Log out of the library
		}

		#endregion Constructors 

		#region Properties (7) 

/// <summary>
		/// Used to create and retrieve book information
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		internal Api.Services.Book BookService
		{
			get
			{
				return svcBook;
			}
		}

		/// <summary>
		/// Gets the configuration object for this <see cref="Importer"/>
		/// </summary>
		public Config ConfigSettings {get { return this._config; } }

		/// <summary>
		/// Used to retrieve base objects and search
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		internal Api.Services.ObjectCommon CommonService
		{
			get
			{
				return svcCommon;
			}
		}

		/// <summary>
		/// The folder in which new objects will be created, passed in by the proxy
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		internal int ImportFolderId
		{
			get
			{
				return importFolderId;
			}
			set
			{
				importFolderId = value;
			}
		}

		/// <summary>
		/// Used to create and retrieve topics and topic text
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		internal Api.Services.Topic TopicService
		{
			get
			{
				return svcTopic;
			}
		}

		/// <summary>
		/// Reference to the &lt;IMPORT_FULL_PATH&gt; variable
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		internal Api.Dto.Variable VariableImportFullPath
		{
			get
			{
				return importFullPathVariable;
			}
		}

		/// <summary>
		/// Reference to the &lt;IMPORT_ID&gt; variable
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		internal Api.Dto.Variable VariableImportId
		{
			get
			{
				return importIdVariable;
			}
		}

		/// <summary>
		/// Used to create and retrieve variable information
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		internal Api.Services.Variable VariableService
		{
			get
			{
				return svcVariable;
			}
		}

		#endregion Properties 

		#region Delegates and Events (8) 

		// Delegates (4) 

		/// <summary>
		/// Delegate for the log event
		/// </summary>
		/// <param name="e">Arguments for the event</param>
		public delegate void LogEventHandler(LogEventArgs e);
		/// <summary>
		/// Delegate for the object created event
		/// </summary>
		/// <param name="ObjectId">ID of the created object</param>
		public delegate void ObjectCreatedEventHandler(int ObjectId);
		/// <summary>
		/// Delegate for the object modified event
		/// </summary>
		/// <param name="ObjectId">ID of the modified object</param>
		public delegate void ObjectModifiedEventHandler(int ObjectId);
		/// <summary>
		/// Delegate for the progress event
		/// </summary>
		/// <param name="Upto">Number of items processed</param>
		/// <param name="Total">Total number of items</param>
		/// <param name="Treenode">XML node being processed</param>
		public delegate void ProgressEventHandler(int Upto, int Total, TreeNode Treenode);
		// Events (4) 

		/// <summary>
		/// Handles the logging event
		/// </summary>
		public event LogEventHandler Log;

		/// <summary>
		/// Called when the plugin creates and Author-it object
		/// </summary>
		public event ObjectCreatedEventHandler ObjectCreated;

		/// <summary>
		/// Called when the plugin modifies an Author-it object
		/// </summary>
		public event ObjectModifiedEventHandler ObjectModified;

		/// <summary>
		/// Called when the plugin processes an item.
		/// </summary>
		public event ProgressEventHandler Progress;

		#endregion Delegates and Events 

		#region Methods (21) 

		// Public Methods (6) 

		/// <summary>
		///
		/// </summary>
		/// <param name="ObjectType"></param>
		/// <param name="Guid"></param>
		/// <param name="Filename"></param>
		/// <param name="Description"></param>
		/// <returns></returns>
		/// <remarks></remarks>
		public Api.Dto.ObjectBrief GetObjectBriefFromGuid(Api.Dto.ObjectType ObjectType, Guid Guid, string Filename, string Description)
		{
			// Search for all the objects in the library with a variable assignment against the <IMPORT_ID> that matches the ImportId
			// We should only get a single object -- a Warning is created in the log if more than one match is found
			try
			{
				if (_cachedObjectDto.ContainsKey(Guid))
				{
					return _cachedObjectDto[Guid];
				}

				if (svcCommon.CheckExistsByGuid(Guid))
				{
					// an object with this guid already exists
					// TODO: do we need to ensure the object is of correct type ???
					int objectId = svcCommon.GetIdForGuid(Guid);
					try
					{
						// Create variable assignments for this topic against the <IMPORT_ID> and <IMPORT_FULL_PATH> variables
						CreateVariableAssignment(importIdVariable, objectId, Guid.ToString());
						if (Filename.Length > 0)
						{
							CreateVariableAssignment(importFullPathVariable, objectId, Filename.Replace("C:\\Users\\dtomes\\Desktop", "I:"));
						}
					}
					catch (Exception ex)
					{
						if (Log != null)
							Log(new LogEventArgs(Severity.Error, string.Format("Exception creating variable assignments on object {0}", objectId), ex));
					}
					Api.Dto.ObjectBrief objectBrief = svcCommon.GetObjectBrief(objectId);

					_cachedObjectDto.Add(Guid, objectBrief);

					return objectBrief;
				}
				else
				{
					// no object with this guid currently exists in the library
					int newObjectId = 0;

					// set the heading / description to the import is unless something better was passed int
					if (Description.Length == 0)
					{
						Description = Guid.ToString();
					}
					switch (ObjectType)
					{
						case Api.Dto.ObjectType.Book:
							Api.Dto.Book bookDto = new Api.Dto.Book();

							// Set some default properties and save it
							bookDto.Guid = Guid;
							bookDto.Description = Description;
							bookDto.HelpTitle = Description;
							bookDto.PrintTitle = Description;
							bookDto.WebTitle = Description;
							bookDto.FolderId = importFolderId;
							bookDto.BasedOnId = 0;
							bookDto.ReleaseStateId = 0; //TODO: Add configuration setting for default release state
							bookDto = svcBook.Create(bookDto);
							newObjectId = bookDto.ObjectId;
							break;

						case Api.Dto.ObjectType.File:
							break;

						case Api.Dto.ObjectType.Glossary:
							break;

						case Api.Dto.ObjectType.Hyperlink:
							break;

						case Api.Dto.ObjectType.Index:
							break;

						case Api.Dto.ObjectType.IndexEntry:
							break;

						case Api.Dto.ObjectType.Macro:
							break;

						case Api.Dto.ObjectType.Media:
							break;

						case Api.Dto.ObjectType.PublishProfile:
							break;

						case Api.Dto.ObjectType.Style:
							break;

						case Api.Dto.ObjectType.TableOfContents:
							break;

						case Api.Dto.ObjectType.TableOfFigures:
							break;

						case Api.Dto.ObjectType.TitlePage:
							break;

						case Api.Dto.ObjectType.Topic:
							Api.Dto.Topic topicDto = new Api.Dto.Topic();

							// Set some default properties and save it
							topicDto.Guid = Guid;
							topicDto.Description = Description;
							topicDto.HelpHeading = Description;
							topicDto.PrintHeading = Description;
							topicDto.WebHeading = Description;
							topicDto.FolderId = importFolderId;
							topicDto.BasedOnId = 0;
							topicDto.ReleaseStateId = 0; //TODO: Add configuration setting for default release state
							topicDto = svcTopic.Create(topicDto);
							newObjectId = topicDto.ObjectId;
							break;

						default:
							break;
					}

					// a new book or topic has been created, add a <IMPORT_ID> variable assignment so that
					// we can be sure to map to the same object if we re-import
					if (newObjectId > 0)
					{
						// Create variable assignments for this topic against the <IMPORT_ID> and <IMPORT_FULL_PATH> variables
						CreateVariableAssignment(importIdVariable, newObjectId, Guid.ToString());
						CreateVariableAssignment(importFullPathVariable, newObjectId, Filename.Replace("C:\\Users\\dtomes\\Desktop", "I:"));

						if (Log != null)
							Log(new LogEventArgs(Severity.Information, string.Format("Object created ({0}) {1}", newObjectId, Description)));
						if (ObjectCreated != null)
							ObjectCreated(newObjectId);
						return svcCommon.GetObjectBriefByGuid(Guid);
					}
					else
					{
						// we should never hit this...
						if (Log != null)
							Log(new LogEventArgs(Severity.Error, string.Format("GetTopicFromGuid create object for '{0}'", Guid)));
						return null;
					}
				}
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, string.Format("GetTopicFromGuid exception for '{0}'", Guid, ex)));
				return null;
			}
		}

		/// <summary>
		/// Hands off the file to be imported to the appropriate importer based on file extension.
		/// </summary>
		/// <param name="FileName"></param>
		/// <returns></returns>
		/// <remarks>True if the file imports</remarks>
		public bool ImportFile(string FileName)
		{
			// select an appropriate importer based on the file extension
			if (Log != null)
				Log(new LogEventArgs(Severity.Information, string.Format("Start importing file '{0}'", System.IO.Path.GetFileName(FileName))));
			switch (System.IO.Path.GetExtension(FileName).ToLower())
			{
				case ".hxt":
					return ImportHxt(FileName);
				case ".xml":
				case ".dita":
					return ImportDita(FileName);
				default:
					if (Log != null)
						Log(new LogEventArgs(Severity.Warning, string.Format("Extension not recognised for file '{0}'", FileName)));
					return false;
			}
		}

		/// <summary>
		/// Closes the library
		/// </summary>
		/// <remarks></remarks>
		public void LogOut()
		{
			try
			{
				if (_authentication != null && _authentication.IsLoggedIn())
				{
					_authentication.Logout();
				}
			}
			catch (InvalidOperationException)
			{
				//TODO: Log a logout failure if we implement that level of logging for the plugin
			}
			finally
			{
				svcBook = null;
				svcCommon = null;
				svcHyperlink = null;
				svcTopic = null;
				svcVariable = null;
				_authentication = null;
			}
		}

		/// <summary>
		/// Uses the &lt;IMPORT_FULL_PATH&gt; variable assignmnet to locate and re-import a source file
		/// </summary>
		/// <param name="ObjectId"></param>
		/// <returns></returns>
		/// <remarks>May cause multiple objects to re-import if the file creates embedded topics</remarks>
		public bool ReImportObject(int ObjectId)
		{
			// get the path to the file the object was imported from
			string sourceFileName = "";
			try
			{
				// get the path to the source file from the <IMPORT_FULL_PATH> variable assignment
				sourceFileName = GetSourceFile(ObjectId);
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, string.Format("Exception retrieving variable assignment for object ({0})", ObjectId), ex));
			}

			// check if the file exists
			if (sourceFileName.Length == 0)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Warning, string.Format("Skipped object ({0}) -- <IMPORT_FULL_PATH> variable assignment is empty", ObjectId)));
			}
			else if (System.IO.File.Exists(sourceFileName))
			{
				// re-import the object
				if (ImportFile(sourceFileName))
				{
					if (ObjectModified != null)
						ObjectModified(ObjectId);
				}
			}
			else
			{
				// file not found
				if (Log != null)
					Log(new LogEventArgs(Severity.Warning, string.Format("Skipped object ({0}) -- File '{1}' not found", ObjectId, sourceFileName)));
			}
			return false;
		}

		/// <summary>
		/// Appends a string of Author-it Topic Text XML appropriate for the heading row of a two column table
		/// </summary>
		/// <param name="StringBuilder"></param>
		/// <param name="ColumnOneHeading">Heading for column one</param>
		/// <param name="ColumnTwoHeading">Heading for column two</param>
		/// <remarks></remarks>
		public void TextTableHeader2ColumnXml(System.Text.StringBuilder StringBuilder, string ColumnOneHeading, string ColumnTwoHeading)
		{
			StringBuilder.AppendFormat("<tr header=\"true\" rowheight=\"0\" >" + "<td width=\"2500\" borders=\"15\" bordercolors=\"12632256\" bgcolor=\"14211288\" >" + "<p id=\"{0}\" >{1}</p>" + "</td>" + "<td width=\"7500\" borders=\"15\" bordercolors=\"12632256\" bgcolor=\"14211288\" >" + "<p id=\"{0}\" >{2}</p>" + "</td>" + "</tr>", this.GetObjectId(Properties.Settings.Default.StyleTableHeading), ColumnOneHeading, ColumnTwoHeading);
		}

		/// <summary>
		/// Appends a string of Author-it Topic Text Xml appropriate for the heading row of a two column table
		/// </summary>
		/// <param name="StringBuilder">The string builder to use to append the table to</param>
		/// <param name="ColumnOneHeading">Heading for column one</param>
		/// <param name="ColumnThreeHeading">Heading for column two</param>
		/// <param name="ColumnTwoHeading">Heading for column three</param>
		/// <remarks></remarks>
		public void TextTableHeader3ColumnSummaryXml(System.Text.StringBuilder StringBuilder, string ColumnOneHeading, string ColumnTwoHeading, string ColumnThreeHeading)
		{
			StringBuilder.AppendFormat("<tr header=\"true\" rowheight=\"0\" >" + "<td width=\"500\" borders=\"15\" bordercolors=\"12632256\" bgcolor=\"14211288\" >" + "<p id=\"{0}\" >{1}</p>" + "</td>" + "<td width=\"3500\" borders=\"15\" bordercolors=\"12632256\" bgcolor=\"14211288\" >" + "<p id=\"{0}\" >{2}</p>" + "</td>" + "<td width=\"6000\" borders=\"15\" bordercolors=\"12632256\" bgcolor=\"14211288\" >" + "<p id=\"{0}\" >{3}</p>" + "</td>" + "</tr>", this.GetObjectId(Properties.Settings.Default.StyleTableHeading), ColumnOneHeading, ColumnTwoHeading, ColumnThreeHeading);
		}
		// Private Methods (8) 

		/// <summary>
		/// Created to import HxT files but can probably be converted to import dita toc structures.
		/// </summary>
		/// <param name="book"></param>
		/// <param name="parentContentNode">Pass in Null/Nothing for root nodes so they will be added to the book at the root level</param>
		/// <param name="xmlNode"></param>
		/// <remarks></remarks>
		private void BuildBookTableOfContents(ref Api.Dto.Book book, ref Api.Dto.BookTocEntry parentContentNode, System.Xml.XmlNode xmlNode)
		{
			//If xmlNode.ChildNodes.Count > 0 Then
			//    For Each childNode As Xml.xmlNode In xmlNode.ChildNodes
			//        If childNode.Name = "HelpTOCNode" Then
			//            Dim url As String = ""
			//            Dim objectGuid As Guid
			//            Dim title As String = ""
			//            Dim tocEntry As API.Dto.BookTocEntry = Nothing

			//            ' get the url and page title
			//            If childNode.Attributes IsNot Nothing AndAlso childNode.Attributes("Url") IsNot Nothing Then
			//                url = childNode.Attributes("Url").Value
			//                objectGuid = New Guid(IO.Path.GetFileNameWithoutExtension(url))
			//            End If
			//            If childNode.Attributes IsNot Nothing AndAlso childNode.Attributes("Title") IsNot Nothing Then
			//                title = childNode.Attributes("Title").Value
			//            End If

			//            ' we must have a url / page title to make it worth importing the node
			//            If url.Length > 0 And title.Length > 0 Then
			//                'Dim targetObject As API.Dto.ObjectBrief = GetObjectBriefFromImportId(API.Dto.ObjectType.Topic, url, title)
			//                Dim targetObject As API.Dto.ObjectBrief = GetObjectBriefFromGuid(API.Dto.ObjectType.Topic, objectGuid, url, title)
			//                tocEntry = New API.Dto.BookTocEntry
			//                tocEntry.ObjectId = targetObject.ObjectId

			//                ' see if it's a root tocEntry, or the child of another tocEntry
			//                If ParentContentNode Is Nothing Then
			//                    Book.TableOfContents.Add(tocEntry)
			//                Else
			//                    ParentContentNode.Children.Add(tocEntry)
			//                End If
			//                RaiseEvent Log(New ImportLogEventArgs(Severity.Error, "Added '" & targetObject.Description & "' to book contents"))
			//                Application.DoEvents()
			//                GC.Collect()
			//            End If

			//            ' recurse through xml nodes
			//            If childNode.HasChildNodes Then
			//                BuildBookTableOfContents(Book, tocEntry, childNode)
			//            End If
			//        End If
			//    Next
			//End If
		}

		/// <summary>
		/// Finds a release state by its object guid
		/// </summary>
		/// <param name="Guid">Author-it release state guid</param>
		/// <returns>The release state data transfer object</returns>
		/// <remarks>If a release state is not found nothing is returned</remarks>
		private Api.Dto.ReleaseState GetReleaseState(Guid Guid)
		{
			// store a cache of all the release states in the library indexed by guid
			// see if the cache has been loaded yet
			if (releaseStateCache.Count == 0)
			{
				// load all the release states
				Api.Services.ReleaseState releaseStateService = _applicationContext.GetServiceInstance<Api.Services.ReleaseState>();
				foreach (Api.Dto.ReleaseState releaseState in releaseStateService.GetAll())
				{
					// add them to a cache and index them by guid
					releaseStateCache.Add(releaseState.Guid, releaseState);
				}
			}

			// see if the cache contains a release state with this guid
			if (releaseStateCache.ContainsKey(Guid))
			{
				// it does, return it
				return releaseStateCache[Guid];
			}
			else
			{
				// it's missing, raise a message to the log and return nothing
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, string.Format("Cannot find release state with guid ({0})", Guid.ToString())));
				return null;
			}
		}

		private bool ImportDita(string Filename)
		{
			try
			{
				// Open the file as an xmlDocument
				System.Xml.XmlDocument xmlDocument = LoadXmlDocument(Filename);
				if (xmlDocument != null)
				{
					// get all the topic nodes in the xmlDocument (there should probably only be one)
					System.Xml.XmlNodeList xmlNodeList = xmlDocument.GetElementsByTagName("topic");

					// only import the first node, the rest come in as embedded topics
					ImportTopicNode(xmlNodeList[0], Filename, false);
				}
				return true;
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, string.Format("Import Xml exception during file '{0}'", Filename), ex));
				return false;
			}
		}

		/// <summary>
		/// Created to parse HxT files can probably be converted to import .dita contents files.
		/// </summary>
		/// <param name="Filename"></param>
		/// <returns></returns>
		/// <remarks></remarks>
		private bool ImportHxt(string Filename)
		{
			//Try
			//    ' open the Hxt file as an xml document
			//    Dim xmlDocument As Xml.xmlDocument = LoadXmlDocument(Filename)

			//    ' parse the xml document
			//    If xmlDocument IsNot Nothing Then
			//        Try
			//            Dim bookBrief As API.Dto.ObjectBrief = GetObjectBriefFromImportId(API.Dto.ObjectType.Book, Filename, IO.Path.GetFileName(Filename))
			//            If bookBrief IsNot Nothing AndAlso bookBrief.ObjectType = API.Dto.ObjectType.Book Then
			//                Dim bookDto As API.Dto.Book = svcBook.Get(bookBrief.ObjectId)
			//                'When looping through the child nodes, the anchor tags must be treated differently to all other tags

			//                'In the TOC file, all the topics are contained within nodes in the HelpTOC node
			//                Dim xmlBodyNode As xmlNode = xmlDocument.SelectSingleNode("HelpTOC")
			//                If xmlBodyNode IsNot Nothing Then
			//                    bookDto.TableOfContents.Clear()
			//                    BuildBookTableOfContents(bookDto, Nothing, xmlBodyNode)
			//                    svcBook.Update(bookDto)
			//                End If
			//            End If
			//        Catch thirdException As Exception
			//            RaiseEvent Log(New ImportLogEventArgs(Severity.Error, String.Format("Import Hxt exception parsing document '{0}'", Filename), thirdException))
			//        End Try
			//    End If

			//    Return True
			//Catch ex As Exception
			//    RaiseEvent Log(New ImportLogEventArgs(Severity.Error, String.Format("Import Hxt exception during file '{0}'", Filename), ex))
			//    Return False
			//End Try
			return false;
		}

		/// <summary>
		/// Creates a single topic from DITA / XML. But may recurse to create embedded topics
		/// </summary>
		/// <param name="xmlNode">The &lt;topic&gt; node that is the base DITA node of the topic to be imported</param>
		/// <param name="fileName">The name of the file being imported (stored in &lt;IMPORT_FULL_PATH&gt; variable assignment</param>
		/// <param name="embeddedTopic">causes the topic heading to be included as a sub-heading in the topic text</param>
		/// <returns>The topic id of the topic created</returns>
		/// <remarks></remarks>
		private int ImportTopicNode(System.Xml.XmlNode xmlNode, string fileName, bool embeddedTopic)
		{
			try
			{
				// All imported content needs a uniqu id that we can store in the <IMPORT_ID> variable assignment
				// this allows mapping of links, toc and re-importing

				// get the unique import id (for example <topic id="topic_6EF44B0A364943E9B61A78F67391075B"> )
				string importId = xmlNode.Attributes["id"].Value;

				//HACK: I'm assuming that the first child node has the <title> or equivilent node in it to use as
				//      a topic heading / description probably this should be an XPath statement
				string topicTitle = xmlNode.ChildNodes[0].InnerText;

				// Either create a new empty topic, or return an existing one with the same <IMPORT_ID> assignment and overwrite it
				Api.Dto.ObjectBrief topicBrief = GetObjectBriefFromImportId(Api.Dto.ObjectType.Topic, importId, topicTitle, false);
				if (topicBrief != null)
				{
					importTopic = new DitaTopic(this);

					// parse the DITA to create the topic text
					importTopic.ImportTopicText(topicBrief.ObjectId, xmlNode, fileName, importId, embeddedTopic);
				}

				return topicBrief.ObjectId;
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, string.Format("ImportTopicNode exception during file '{0}'", fileName), ex));
				return 0;
			}
		}

		// TODO: Remove? was only needed for MS because we were finding imported topics by guid
		//Friend Function GetHypertextLinkObjectBriefFromGuid(ByVal Guid As Guid) As API.Dto.ObjectBrief
		//    ' TODO: this function would be better if it looked for all the existing hypertext links that targeted a specific
		//    ' topic based on the topic guid. Unfortunately, there is no elegant way to do this through the Author-it SDK
		//    ' so instead, we are storing a value in the <IMPORT_ID> that (all going well) is close enough.
		//    ' Look for (or creates) a Hypertext Link Object that targets a Topic with the Guid passed in.
		//    ' Create a unique <IMPORT_ID> for the hypertext link
		//    Dim importId As String = "href:" & Guid.ToString
		//    Try
		//        ' create a list of search criteria
		//        Dim searchCriteria As New Collections.Generic.List(Of API.Dto.ObjectSearchCriteria)
		//        ' Create a search criteria to look for all objects where the <IMPORT_ID> variable assignment is equal to the ImportId
		//        Dim variableSearchCriteria As New API.Dto.ObjectSearchCriteria()
		//        variableSearchCriteria.Field = API.Dto.ObjectSearchField.VariableAssignment
		//        variableSearchCriteria.Value = String.Format("<IMPORT_ID>={0}", importId)
		//        searchCriteria.Add(variableSearchCriteria)
		//        ' Get all the objects with this variable assignment
		//        Dim objectBriefs As Collections.Generic.IList(Of API.Dto.ObjectBrief) = svcCommon.Search(searchCriteria, _
		//                                                                                                            API.Dto.ObjectListSort.CreatedDate, _
		//                                                                                                            API.Dto.SortDirection.Ascending)
		//        If objectBriefs Is Nothing OrElse objectBriefs.Count = 0 Then
		//            ' There is no existing hypertext link with <IMPORT_ID>="href=guid-guid-guid-guidguid"
		//            ' Get the topic (or create the topic placeholder)
		//            Dim topicDto As API.Dto.ObjectBrief = GetObjectBriefFromGuid(API.Dto.ObjectType.Topic, Guid, "", Guid.ToString)
		//            Dim hypertextLinkDto As New API.Dto.Hyperlink
		//            ' Set some default properties based on the target topic and save it
		//            With hypertextLinkDto
		//                .Description = topicDto.Description
		//                .TopicTargets.Add(topicDto.ObjectId)
		//                .FolderId = importFolderId
		//                .BasedOnId = GetObjectId(My.Settings.HypertextLinkJumpTemplate)
		//                .ReleaseStateId = 0 ' Me.GetReleaseStateId(My.Settings.ReleaseStateImported)
		//            End With
		//            hypertextLinkDto = svcHyperlink.Create(hypertextLinkDto)
		//            If hypertextLinkDto.ObjectId > 0 Then
		//                ' Create variable assignments for this topic against the <IMPORT_ID> variable
		//                CreateVariableAssignment(importIdVariable, hypertextLinkDto.ObjectId, importId)
		//                RaiseEvent Log(New ImportLogEventArgs(Severity.Information, String.Format("Hypertext link created ({0}) {1}", hypertextLinkDto.ObjectId, hypertextLinkDto.Description)))
		//                'RaiseEvent ObjectCreated(hypertextLinkDto.ObjectId)
		//                Return svcCommon.GetObjectBrief(hypertextLinkDto.ObjectId)
		//            Else
		//                ' we should never hit this...
		//                RaiseEvent Log(New ImportLogEventArgs(Severity.Error, String.Format("GetHypertextLinkObjectBriefFromGuid create object for <IMPORT_ID>='{0}'", importId)))
		//                Return Nothing
		//            End If
		//        Else
		//            If objectBriefs.Count > 1 Then
		//                RaiseEvent Log(New ImportLogEventArgs(Severity.Warning, String.Format("Multiple object matches for <IMPORT_ID>='{0}'", importId)))
		//            End If
		//            Return objectBriefs(0)
		//        End If
		//    Catch ex As Exception
		//        RaiseEvent Log(New ImportLogEventArgs(Severity.Error, String.Format("GetHypertextLinkObjectBriefFromGuid exception for <IMPORT_ID>='{0}'", importId, ex)))
		//        Return Nothing
		//    End Try
		//End Function
		//TODO: re-write for DITA
		//Friend Function GetHypertextExternalLinkObjectBriefFromGuid(ByVal Url As String, ByVal Description As String) As API.Dto.ObjectBrief
		//    ' TODO: this function would be better if it looked for all the existing hypertext links that targeted a specific
		//    ' topic based on the topic guid. Unfortunately, there is no elegant way to do this through the Author-it SDK
		//    ' so instead, we are storing a value in the <IMPORT_ID> that (all going well) is close enough.
		//    ' Look for (or creates) a Hypertext Link Object that targets a Topic with the Guid passed in.
		//    ' Create a unique <IMPORT_ID> for the hypertext link
		//    Dim importId As String = "href:" & Url.Replace("=", "")
		//    Try
		//        ' create a list of search criteria
		//        Dim searchCriteria As New Collections.Generic.List(Of API.Dto.ObjectSearchCriteria)
		//        ' Create a search criteria to look for all objects where the <IMPORT_ID> variable assignment is equal to the ImportId
		//        Dim variableSearchCriteria As New API.Dto.ObjectSearchCriteria()
		//        variableSearchCriteria.Field = API.Dto.ObjectSearchField.VariableAssignment
		//        variableSearchCriteria.Value = String.Format("<IMPORT_ID>={0}", importId)
		//        searchCriteria.Add(variableSearchCriteria)
		//        ' Get all the objects with this variable assignment
		//        Dim objectBriefs As Collections.Generic.IList(Of API.Dto.ObjectBrief) = svcCommon.Search(searchCriteria, _
		//                                                                                                            API.Dto.ObjectListSort.CreatedDate, _
		//                                                                                                            API.Dto.SortDirection.Ascending)
		//        If objectBriefs Is Nothing OrElse objectBriefs.Count = 0 Then
		//            ' There is no existing hypertext link with <IMPORT_ID>="href:http://blahblah.com"
		//            ' Get the topic (or create the topic placeholder)
		//            Dim hypertextLinkDto As New API.Dto.Hyperlink
		//            ' Set some default properties based on the target topic and save it
		//            With hypertextLinkDto
		//                .Description = Description
		//                ' add the Url to the hypertext link
		//                Dim target As New API.Dto.HyperlinkWebTarget()
		//                target.Description = Description
		//                target.Url = Url
		//                .WebTargets.Add(target)
		//                .FolderId = importFolderId
		//                .BasedOnId = GetObjectId(My.Settings.HypertextLinkJumpTemplate)
		//                .ReleaseStateId = 0 'Me.GetReleaseStateId(My.Settings.ReleaseStateImported)
		//            End With
		//            hypertextLinkDto = svcHyperlink.Create(hypertextLinkDto)
		//            If hypertextLinkDto.ObjectId > 0 Then
		//                ' Create variable assignments for this topic against the <IMPORT_ID> variable
		//                CreateVariableAssignment(importIdVariable, hypertextLinkDto.ObjectId, importId)
		//                RaiseEvent Log(New ImportLogEventArgs(Severity.Information, String.Format("Hypertext link created ({0}) {1}", hypertextLinkDto.ObjectId, hypertextLinkDto.Description)))
		//                'RaiseEvent ObjectCreated(hypertextLinkDto.ObjectId)
		//                Return svcCommon.GetObjectBrief(hypertextLinkDto.ObjectId)
		//            Else
		//                ' we should never hit this...
		//                RaiseEvent Log(New ImportLogEventArgs(Severity.Error, String.Format("Create object for <IMPORT_ID>='{0}'", importId)))
		//                Return Nothing
		//            End If
		//        Else
		//            If objectBriefs.Count > 1 Then
		//                RaiseEvent Log(New ImportLogEventArgs(Severity.Warning, String.Format("Multiple object matches for <IMPORT_ID>='{0}'", importId)))
		//            End If
		//            Return objectBriefs(0)
		//        End If
		private void TopicImportCreated(int ObjectId)
		{
			if (ObjectCreated != null)
				ObjectCreated(ObjectId);
		}

		//    Catch ex As Exception
		//        RaiseEvent Log(New ImportLogEventArgs(Severity.Error, String.Format("Exception for <IMPORT_ID>='{0}'", importId, ex)))
		//        Return Nothing
		//    End Try
		//End Function
		private void TopicImportLog(LogEventArgs e)
		{
			if (Log != null)
				Log(e);
		}

		private void TopicImportModified(int ObjectId)
		{
			if (ObjectModified != null)
				ObjectModified(ObjectId);
		}
		// Internal Methods (7) 

		/// <summary>
		/// Creates a variable assignment against an object
		/// </summary>
		/// <param name="Variable">The variable to make the assignment against</param>
		/// <param name="ObjectId">The object to make the assignment against</param>
		/// <param name="Value">The variable value to assign</param>
		/// <remarks></remarks>
		internal void CreateVariableAssignment(Api.Dto.Variable Variable, int ObjectId, string Value)
		{
			try
			{
				Api.Dto.VariableAssignment variableAssignmentTokenDescription = new Api.Dto.VariableAssignment();
				variableAssignmentTokenDescription.VariableId = Variable.VariableId;
				variableAssignmentTokenDescription.ObjectId = ObjectId;
				variableAssignmentTokenDescription.Value = Value;
				svcVariable.Assign(variableAssignmentTokenDescription);
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Warning, string.Format("Exception creating variable assignment for object ({0}) of {1}='{2}'", ObjectId, Variable.Description, Value), ex));
			}
		}

		/// <summary>
		/// Creates an embedded topic from a second or subsequent DITA &lt;topic&gt; node
		/// </summary>
		/// <param name="xmlNode">The &lt;topic&gt; node that is the base DITA node of the topic to be imported</param>
		/// <param name="fileName">The file the topic is being imported from</param>
		/// <returns>The object id of the created topic</returns>
		/// <remarks>Overwrites an existing topic if &lt;IMPORT_ID&gt; matches</remarks>
		internal int GetEmbeddedTopicId(System.Xml.XmlNode xmlNode, string fileName)
		{
			return ImportTopicNode(xmlNode, fileName, true);
		}

		/// <summary>
		/// Finds an object based on the &lt;IMPORT_ID&gt; variable assignment
		/// </summary>
		/// <param name="ObjectType">Filters by this object type</param>
		/// <param name="ImportId">Matches on this import id</param>
		/// <param name="Description">Sets the description if an object needs to be created</param>
		/// <param name="IsTemplate">Filters by being a template</param>
		/// <returns>An object brief data transfer object</returns>
		/// <remarks>If the object doesn't exist it is created and the import id set</remarks>
		internal Api.Dto.ObjectBrief GetObjectBriefFromImportId(Api.Dto.ObjectType ObjectType, string ImportId, string Description = "", bool IsTemplate = false)
		{
			// Search for all the objects in the library with a variable assignment against the <IMPORT_ID> that matches the ImportId
			// We should only get a single object -- a Warning is created in the log if more than one match is found
			try
			{
				// create a list of search criteria
				List<Api.Dto.ObjectSearchCriteria> searchCriteria = new List<Api.Dto.ObjectSearchCriteria>();

				if (IsTemplate)
				{
					// Create a search criteria to search for templates
					Api.Dto.ObjectSearchCriteria variableSearchCriteriaIncludeTemplates = new Api.Dto.ObjectSearchCriteria();
					variableSearchCriteriaIncludeTemplates.Field = Api.Dto.ObjectSearchField.IsTemplate;
					variableSearchCriteriaIncludeTemplates.Value = "true";
					searchCriteria.Add(variableSearchCriteriaIncludeTemplates);
				}

				// Create a search criteria to look for all objects where the <IMPORT_ID> variable assignment is equal to the ImportId
				Api.Dto.ObjectSearchCriteria variableSearchCriteria = new Api.Dto.ObjectSearchCriteria();
				variableSearchCriteria.Field = Api.Dto.ObjectSearchField.VariableAssignment;
				variableSearchCriteria.Value = "<IMPORT_ID>=" + ImportId;
				searchCriteria.Add(variableSearchCriteria);

				// Get all the objects with this variable assignment
				IList<Api.Dto.ObjectBrief> objectBriefs = svcCommon.Search(searchCriteria, Api.Dto.ObjectListSort.CreatedDate, Api.Dto.SortDirection.Ascending);
				if (objectBriefs == null || objectBriefs.Count == 0)
				{
					// there is no existing object, create one
					int newObjectId = 0;

					// set the heading / description to the import is unless something better was passed int
					if (Description.Length == 0)
					{
						Description = ImportId;
					}
					switch (ObjectType)
					{
						case Api.Dto.ObjectType.Book:
							Api.Dto.Book bookDto = new Api.Dto.Book();

							// Set some defult properties and save it
							//.Guid = ObjectGuid
							bookDto.Description = Description;
							bookDto.HelpTitle = Description;
							bookDto.PrintTitle = Description;
							bookDto.WebTitle = Description;
							bookDto.FolderId = importFolderId;

							//TODO: assign appropriate template and set release state to 'imported'
							bookDto.BasedOnId = 0;
							bookDto.ReleaseStateId = 0; //Me.GetReleaseStateId(My.Settings.ReleaseStatePlaceholder)

							bookDto = svcBook.Create(bookDto);
							newObjectId = bookDto.ObjectId;
							break;

						case Api.Dto.ObjectType.File:
							break;

						case Api.Dto.ObjectType.Glossary:
							break;

						case Api.Dto.ObjectType.Hyperlink:
							break;

						case Api.Dto.ObjectType.Index:
							break;

						case Api.Dto.ObjectType.IndexEntry:
							break;

						case Api.Dto.ObjectType.Macro:
							break;

						case Api.Dto.ObjectType.Media:
							break;

						case Api.Dto.ObjectType.PublishProfile:
							break;

						case Api.Dto.ObjectType.Style:
							break;

						case Api.Dto.ObjectType.TableOfContents:
							break;

						case Api.Dto.ObjectType.TableOfFigures:
							break;

						case Api.Dto.ObjectType.TitlePage:
							break;

						case Api.Dto.ObjectType.Topic:
							Api.Dto.Topic topicDto = new Api.Dto.Topic();

							// Set some default properties and save it
							//.Guid = ObjectGuid
							topicDto.Description = Description;
							topicDto.HelpHeading = Description;
							topicDto.PrintHeading = Description;
							topicDto.WebHeading = Description;
							topicDto.FolderId = importFolderId;

							//TODO: assign appropriate template and set release state to 'imported'
							topicDto.BasedOnId = 0;
							topicDto.ReleaseStateId = 0; // Me.GetReleaseStateId(My.Settings.ReleaseStatePlaceholder)
							topicDto = svcTopic.Create(topicDto);
							newObjectId = topicDto.ObjectId;
							break;

						default:
							break;
					}

					// a new book or topic has been created, add a <IMPORT_ID> variable assignment so that
					// we can be sure to map to the same object if we re-import
					if (newObjectId > 0)
					{
						// Create variable assignments for this topic against the <IMPORT_ID> variable
						CreateVariableAssignment(importIdVariable, newObjectId, ImportId);
						CreateVariableAssignment(importFullPathVariable, newObjectId, ImportId);
						if (Log != null)
							Log(new LogEventArgs(Severity.Information, string.Format("Object created ({0}) {1}", newObjectId, Description)));
						if (ObjectCreated != null)
							ObjectCreated(newObjectId);
						return svcCommon.GetObjectBrief(newObjectId);
					}
					else
					{
						// we should never hit this...
						if (Log != null)
							Log(new LogEventArgs(Severity.Error, string.Format("GetTopicFromImportId create object for <IMPORT_ID>='{0}'", ImportId)));
						return null;
					}
				}
				else
				{
					if (objectBriefs.Count > 1)
					{
						if (Log != null)
							Log(new LogEventArgs(Severity.Warning, string.Format("Multiple object matches for <IMPORT_ID>='{0}'", ImportId)));
					}
					return objectBriefs[0];
				}
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, string.Format("GetTopicFromImportId exception for <IMPORT_ID>='{0}'", ImportId, ex)));
				return null;
			}
		}

		/// <summary>
		/// Gets an AIT object id based on its GUID
		/// </summary>
		/// <param name="Guid">The object GUID from the AIT object to be used</param>
		/// <returns>object id</returns>
		/// <remarks>GUIDs are more portable between libraries than object ids</remarks>
		internal int GetObjectId(Guid Guid)
		{
			if (objectIdCache.ContainsKey(Guid))
			{
				return objectIdCache[Guid];
			}
			else if (svcCommon.CheckExistsByGuid(Guid))
			{
				int objectId = svcCommon.GetIdForGuid(Guid);
				objectIdCache.Add(Guid, objectId);
				return objectId;
			}
			else
			{
				throw new Exception(string.Format("Cannot find object with guid ({0})", Guid.ToString()));
			}
		}

		/// <summary>
		/// gets the release state id from its guid
		/// </summary>
		/// <param name="Guid">AIT release state guid</param>
		/// <returns>AIT release state id</returns>
		/// <remarks>Guids are more portable between libraries than release state ids</remarks>
		internal int GetReleaseStateId(Guid Guid)
		{
			Api.Dto.ReleaseState releaseState = GetReleaseState(Guid);
			if (releaseState == null)
			{
				return 0;
			}
			else
			{
				return releaseState.ReleaseStateId;
			}
		}

		/// <summary>
		/// Returns the variable assignment in &lt;IMPORT_FULL_PATH&gt; if one exists
		/// </summary>
		/// <param name="ObjectId"></param>
		/// <returns></returns>
		/// <remarks></remarks>
		internal string GetSourceFile(int ObjectId)
		{
			// see if there is an existing variable assignment against the <IMPORT_FULL_PATH> variable
			try
			{
				Api.Dto.VariableAssignment va = new Api.Dto.VariableAssignment();
				va.ObjectId = ObjectId;
				va.VariableId = importFullPathVariable.VariableId;

				if (svcVariable.CheckAssignmentExists(va))
				{
					// the object has a variable assignment for <IMPORT_FULL_PATH>
					string sourceFileName = svcVariable.GetAssignment(ObjectId, importFullPathVariable.VariableId).Value;
					return sourceFileName;
				}
				else
				{
					// there is no variable assignment for <IMPORT_FULL_PATH> for this object
					if (Log != null)
						Log(new LogEventArgs(Severity.Warning, string.Format("There is no <IMPORT_FULL_PATH> variable assignment for object ({0})", ObjectId)));
					return string.Empty;
				}
			}
			catch (Exception ex)
			{
				if (Log != null)
					Log(new LogEventArgs(Severity.Error, string.Format("Exception in GetSourceFile() for object ({0}) -- {1}", ObjectId, ex.Message), ex));
				return string.Empty;
			}
		}

		/// <summary>
		/// Loads the file into an xmlDocument
		/// </summary>
		/// <param name="Filename"></param>
		/// <returns>The file as an xmlDocument</returns>
		/// <remarks>If it fails, it will attempt to remove the DTD and try again (this is the most common problem we've struck opening XmlDocuments)</remarks>
		internal System.Xml.XmlDocument LoadXmlDocument(string Filename)
		{
			System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();
			try
			{
				xmlDocument.PreserveWhitespace = false;
				xmlDocument.Load(Filename);
			}
			catch (Exception firstException)
			{
				// An exception was thrown loading the file into an xmlDocument - apply series of hacks and try again:

				//HACK: Remove the path to the dtd path and try re-loading
				if (Log != null)
					Log(new LogEventArgs(Severity.Warning, string.Format("Removing dtd reference from '{0}'", Filename), firstException));

				// load xml document as a string
				string xmlstring = File.ReadAllText(Filename);

				//Find where the reference to the dtd file is in the string and remove it
				int dtdFileExtensionPosition = xmlstring.IndexOf(".dtd");
				int dtdPosition = xmlstring.LastIndexOf("\"", dtdFileExtensionPosition) + 1;
				string newXmlString = xmlstring.Substring(0, dtdPosition) + xmlstring.Substring(dtdFileExtensionPosition + 4);

				//HACK: xmlDocument.Load() throws exception on &nbsp; as undeclared element
				newXmlString = newXmlString.Replace("&nbsp;", " ");

				// Now try re-loading the xmlDocument from the hacked string:
				XmlReaderSettings settings = new XmlReaderSettings();
				settings.XmlResolver = null;
				settings.ProhibitDtd = false;
				XmlReader reader = XmlReader.Create(new System.IO.StringReader(newXmlString), settings);
				try
				{
					//TODO: we need a better way to solve white space issues - this isn't working
					xmlDocument.PreserveWhitespace = false;
					xmlDocument.Load(reader);
					xmlDocument.PreserveWhitespace = false;
				}
				catch (Exception secondException)
				{
					// Now we're really stuffed - none of the hacks worked - try creating another hack :)
					if (Log != null)
						Log(new LogEventArgs(Severity.Error, string.Format("Cannot open Xml Document '{0}'", Filename), secondException));
					return null;
				}
			}
			return xmlDocument;
		}

		#endregion Methods 
	}
}